﻿/*
 * Author: Martijn Koopman
 * 
 * Description: Simple weight-based template matching algorithm
 * 
 * */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;

namespace LibraryCS {
    public class OverlayTemplateMatching : VisionAlgorithm {

        private Boolean useDirectory;
        private String testsetDir, outputDir, templateDir = TemplatePreparation.templateDir;
        private List<VImage> testsetBitmaps = new List<VImage>();
        private List<VImage> templateBitmaps = TemplatePreparation.templates;
        private double[] distinctiveImage; 
        private List<String> percentages = new List<String>();
        private String endResult = "";

        public OverlayTemplateMatching(String name)
            : base(name) {
            // Create distinctive image
            createDistinctiveImage();
        }

        public override Bitmap DoAlgorithm(Bitmap sourceImage) {
            endResult = "";
            // Important assumption!!!
            // The bitmap to compare has already been cropped and scaled

            if (templateBitmaps.Count < 1) {
                Console.Out.WriteLine("No template images to use (2)");
                return null;
            }

            /* 
            // Distinctive image to bitmap
            VImage distinctiveBitmap = new VImage(templateImages[0].Width, templateImages[0].Height);
            double intensityIncrement = 1.0 / (double)templateImages.Count;
            for (int i = 0; i < templateImages.Count; i++) {
                for (int j = 0; j < templateImages[i].RedPixels.Length; j++) {
                    if (templateImages[i].RedPixels[j] == 0) {
                        byte intensity = (byte)((double)255 * (double)intensityIncrement);
                        distinctiveBitmap.RedPixels[j] += intensity;
                        distinctiveBitmap.GreenPixels[j] += intensity;
                        distinctiveBitmap.BluePixels[j] += intensity;
                    }
                }
            }
            distinctiveBitmap.ApplyPixelChanges();
            return distinctiveBitmap.localImage;
            */

            if (useDirectory)
                return processDirectory();
            else {
                VImage image = new VImage(sourceImage);

                // Crop
                image = image.BoundingCrop();

                // Scale
                image = image.Scale(100, 150);

                return processImage(image);
            }
        }

        /*
         * Function written by: Duncan Jenkins
         * */
        public Bitmap processDirectory() {
            for (int i = 0; i < testsetBitmaps.Count; i++) {
                processImage(testsetBitmaps[i]);
            }

            if (File.Exists(outputDir + "classified.csv")) {
                File.Delete(outputDir + "classified.csv");
            }
            StreamWriter sw = new StreamWriter(outputDir + "classified.csv");
            sw.WriteLine("Input file;Matched %;Classified as");
            sw.WriteLine("");

            foreach (string s in percentages) {
                sw.WriteLine(s);
                Debug.WriteLine(s);
            }
            sw.WriteLine("");
            sw.WriteLine("End result;" + endResult);
            Debug.WriteLine("End result;" + endResult);

            sw.Close();

            Bitmap result = new Bitmap(800, 600);
            Graphics g = Graphics.FromImage(result);
            g.Clear(Color.White);
            Font font = new Font(FontFamily.GenericSansSerif, 30, FontStyle.Regular, GraphicsUnit.Pixel);
            g.DrawString(endResult, font, new SolidBrush(Color.Black), 0, 0);
            return result;
        }

        private Bitmap processImage(VImage input) {
            VImage highestSimilarityImage = null;

            //int numPixels = image.Width * image.Height;
            double highestSimilarity = -10000.0;
            for (int i = 0; i < templateBitmaps.Count; i++) {
                double similarity = 0;

                for (int j = 0; j < templateBitmaps[i].RedPixels.Length; j++) {
                    if (templateBitmaps[i].RedPixels[j] == input.RedPixels[j]) {
                        similarity += (1.0 - distinctiveImage[j]);
                    } else {
                        similarity -= (1.0 - distinctiveImage[j]);
                    }
                }

                //Console.Out.WriteLine("Similarity " + i + ", "+templateImages[i].localImage.Tag+": " + similarity);

                if (similarity > highestSimilarity) {
                    highestSimilarity = similarity;
                    highestSimilarityImage = templateBitmaps[i];
                }
            }

            Debug.WriteLine("Character = " + highestSimilarityImage.localBitmap.Tag);
            endResult += highestSimilarityImage.localBitmap.Tag;

            return highestSimilarityImage.localBitmap;
        }

        private void createDistinctiveImage() {
            if (templateBitmaps.Count < 1) {
                Console.Out.WriteLine("No template images to use (1)");
                return;
            }

            // Create distintive image
            distinctiveImage = new double[templateBitmaps[0].RedPixels.Length];

            // Draw distinctive image
            double intensityIncrement = 1.0 / (double)templateBitmaps.Count;
            for (int i = 0; i < templateBitmaps.Count; i++) {
                for (int j = 0; j < templateBitmaps[i].RedPixels.Length; j++) {
                    if (templateBitmaps[i].RedPixels[j] == 0) {
                        distinctiveImage[j] += intensityIncrement;
                    }
                }
            }
        }

        #region GUI Functions

        public override SettingsPropertyBox createControls()
        {
            SettingsPropertyBox Controls = new SettingsPropertyBox();

            Label lbUseDir = new Label();
            lbUseDir.AutoSize = true;
            lbUseDir.Text = "Use directory";
            lbUseDir.Name = "lbUseDir";

            CheckBox cbUseDir = new CheckBox();
            cbUseDir.Name = "cbUseDir";
            cbUseDir.Checked = false;

            Label lbTestsetDir = new Label();
            lbTestsetDir.AutoSize = true;
            lbTestsetDir.Text = "Test set directory";
            lbTestsetDir.Name = "lbTestsetDir";

            TextBox tbTestsetDir = new TextBox();
            tbTestsetDir.Name = "tbTestsetDir";
            tbTestsetDir.AcceptsReturn = false;
            tbTestsetDir.Multiline = false;
            tbTestsetDir.Size = new Size(50, 25);
            tbTestsetDir.Text = "..\\..\\..\\Resources\\TestSet_input\\";

            Label lbOutputDir = new Label();
            lbOutputDir.AutoSize = true;
            lbOutputDir.Text = "Output directory";
            lbOutputDir.Name = "lbOutputDir";

            TextBox tbOutputDir = new TextBox();
            tbOutputDir.Name = "tbOutputDir";
            tbOutputDir.AcceptsReturn = false;
            tbOutputDir.Multiline = false;
            tbOutputDir.Size = new Size(50, 25);
            tbOutputDir.Text = "..\\..\\..\\Resources\\TestSet_output\\";

            Controls.AddLine(lbOutputDir, tbOutputDir, null, null);
            Controls.AddLine(lbTestsetDir, tbTestsetDir, null, null);
            Controls.AddLine(lbUseDir, cbUseDir, null, null);

            return Controls;
        }

        public override void setSettings(List<object> settings) {
            try {
                useDirectory = Boolean.Parse((string)settings[0]);
                testsetDir = (string)settings[1];
                outputDir = (string)settings[2];

                // Prepare Test set
                testsetBitmaps = TemplatePreparation.prepareTestSet(testsetDir, outputDir);
            } catch (FormatException) {
                MessageBox.Show("Wrong Format has been entered.\nPlease enter integers\nNew Settings have been discarded for Median", "Format Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
        }

        #endregion
    }
}
